/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.gmi.view.diagram;

import xowl.gmi.environment.Locale;

/**
 *
 * @author Laurent WOUTERS
 */
public class GraphNode extends SelectableWidget {
    public static interface Actions extends SelectableWidget.Actions {
        boolean isConnectionStart();
        boolean isConnectionEnd();
        void startConnection(int portID);
        void endConnection(int portID);
        void onMenuLayerUp();
        void onMenuLayerDown();
        void onMenuLayerTop();
        void onMenuLayerBottom();
        java.awt.datatransfer.DataFlavor getFlavor();
        Object getTransferable(java.awt.Point dragPoint);
    }
    
    private Actions gnActions;
    private Graph graph;
    private xowl.gmi.view.DragHandler dragHandler;
    private Hull hull;
    private boolean isOrigin;
    private int originPort;
    private int nearestPort;
    
    public void setParentGraph(Graph graph) { this.graph = graph; }
    
    public GraphNode(Actions actions, Locale locale) {
        super(actions, locale);
        this.position = new Couple(0, 0);
        this.gnActions = actions;
        this.dragHandler = new xowl.gmi.view.DragHandler() {
            private java.awt.Point dragPoint;
            @Override public int getAction() { return javax.swing.TransferHandler.MOVE; }
            @Override public Object getTransferable() { return GraphNode.this.gnActions.getTransferable(dragPoint); }
            @Override public java.awt.datatransfer.DataFlavor getDataFlavor() { return GraphNode.this.gnActions.getFlavor(); }
            @Override public java.awt.Image getImage() { return getStaticImage(); }
            @Override public void onDragStart(java.awt.Point location) { dragPoint = location; }
            @Override public void onDragDropEnd(Object transfered, int action) { }
        };
        
        javax.swing.JMenuItem itemLayerUp = new javax.swing.JMenuItem(locale.diagLayerUp, xowl.gmi.view.IconProvider.getEmbedded(locale.diagLayerUp_icon));
        itemLayerUp.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override public void mouseReleased(java.awt.event.MouseEvent evt) { GraphNode.this.gnActions.onMenuLayerUp(); }
        });
        javax.swing.JMenuItem itemLayerDown = new javax.swing.JMenuItem(locale.diagLayerDown, xowl.gmi.view.IconProvider.getEmbedded(locale.diagLayerDown_icon));
        itemLayerDown.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override public void mouseReleased(java.awt.event.MouseEvent evt) { GraphNode.this.gnActions.onMenuLayerDown(); }
        });
        javax.swing.JMenuItem itemLayerTop = new javax.swing.JMenuItem(locale.diagLayerTop, xowl.gmi.view.IconProvider.getEmbedded(locale.diagLayerTop_icon));
        itemLayerTop.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override public void mouseReleased(java.awt.event.MouseEvent evt) { GraphNode.this.gnActions.onMenuLayerTop(); }
        });
        javax.swing.JMenuItem itemLayerBottom = new javax.swing.JMenuItem(locale.diagLayerBottom, xowl.gmi.view.IconProvider.getEmbedded(locale.diagLayerBottom_icon));
        itemLayerBottom.addMouseListener(new java.awt.event.MouseAdapter() {
            @Override public void mouseReleased(java.awt.event.MouseEvent evt) { GraphNode.this.gnActions.onMenuLayerBottom(); }
        });
        javax.swing.JMenu menuLayers = new javax.swing.JMenu(locale.diagLayers);
        menuLayers.setIcon(xowl.gmi.view.IconProvider.getEmbedded(locale.diagLayers_icon));
        menuLayers.add(itemLayerUp);
        menuLayers.add(itemLayerDown);
        menuLayers.add(itemLayerTop);
        menuLayers.add(itemLayerBottom);
        this.menu.add(new javax.swing.JSeparator());
        this.menu.add(menuLayers);
    }
    
    public void moveTo(int x, int y) {
        setPosition(new Couple(x, y));
        graph.updateConnectors(this);
    }
    
    private void emitPorts(Couple local) {
        if (hull == null)
            hull = getHull();
        double min = Double.MAX_VALUE;
        for (int i=0; i!=hull.getPorts().size(); i++) {
            double dist = (new Couple(hull.getPorts().get(i), local)).length2();
            if (dist < min) {
                min = dist;
                nearestPort = i;
            }
        }
    }
    
    @Override public boolean onMouseEnter(UserState state, Couple local) {
        if (state == UserState.Dragging) return false;
        if (state == UserState.ConnectionStart) {
            if (!gnActions.isConnectionStart())
                return false;
            emitPorts(local);
            return true;
        }
        if (state == UserState.ConnectionEnd) {
            if (!gnActions.isConnectionEnd())
                return false;
            emitPorts(local);
            return true;
        }
        isHovered = true;
        return true;
    }
    
    @Override public boolean onMouseLeave() {
        if (isOrigin) return true;
        hull = null;
        if (!isHovered) return false; 
        isHovered = false;
        return true;
    }
    
    @Override public boolean isConnectionStart() { return gnActions.isConnectionStart(); }
    @Override public boolean isConnectionEnd() { return gnActions.isConnectionEnd(); }
    @Override public void startConnecting() {
        isOrigin = true;
        originPort = nearestPort;
        gnActions.startConnection(originPort);
    }
    @Override public void clearConnecting() {
        isOrigin = false;
        hull = null;
    }
    @Override public void endConnecting() {
        gnActions.endConnection(nearestPort);
        isOrigin = false;
        hull = null;
    }
    
    @Override public xowl.gmi.view.DragHandler onMouseDrag() { return dragHandler; }
    
    @Override public void paint(java.awt.Graphics2D g2d, float alpha, DiagramStyle style, Couple offset) {
        if (hull == null) {
            super.paint(g2d, alpha, style, offset);
            return;
        }
        sub.paint(g2d, alpha * transparency, style, position.plus(offset));
        for (int i=0; i!=hull.getPorts().size(); i++) {
            if (isOrigin && i == originPort)
                paintPort(g2d, offset, style.portSize, hull.getPorts().get(i), getRealColor(style.portConnectingColor, alpha));
            else if (i == nearestPort)
                paintPort(g2d, offset, style.portSize, hull.getPorts().get(i), getRealColor(style.portHoverColor, alpha));
            else
                paintPort(g2d, offset, style.portSize, hull.getPorts().get(i), getRealColor(style.portColor, alpha));
        }
    }
    
    private void paintPort(java.awt.Graphics2D g2d, Couple offset, int size, Couple point, java.awt.Color color) {
        Couple pos = position.plus(offset).plus(point).minus((double)size / 2);
        g2d.setColor(color);
        g2d.fillOval((int)pos.x, (int)pos.y, size, size);
    }
    
    private void paintNormal(java.awt.Graphics2D g2d, Couple offset, Couple port, Couple normal) {
        Couple begin = position.plus(offset).plus(port);
        Couple end = begin.plus(normal.mult(50));
        g2d.setColor(java.awt.Color.RED);
        g2d.setStroke(new java.awt.BasicStroke(1));
        g2d.drawLine((int)begin.x, (int)begin.y, (int)end.x, (int)end.y);
    }
}
